package com.hk.security.config;


import com.hk.security.authentication.TokenAuthenticationEntryPoint;
import com.hk.security.authentication.TokenAuthenticationProvider;
import com.hk.security.filter.TokenAuthenticationFilter;
import dubhe.token.api.factory.TokenFactory;
import dubhe.token.api.registry.TokenRegistry;
import lombok.Getter;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
@Getter
public class TokenSecurityConfig {

    private final TokenRegistry tokenRegistry;

    private final TokenFactory tokenFactory;

    public TokenSecurityConfig(TokenRegistry tokenRegistry, @Qualifier(TokenFactory.BEAN_NAME) TokenFactory tokenFactory) {
        this.tokenRegistry = tokenRegistry;
        this.tokenFactory = tokenFactory;
    }

    @Bean
    public TokenAuthenticationProvider tokenAuthenticationProvider() {
        return new TokenAuthenticationProvider(tokenRegistry);
    }

    @Bean
    public TokenAuthenticationEntryPoint tokenAuthenticationEntryPoint() {
        return new TokenAuthenticationEntryPoint();
    }

    @Bean
    @Order(2)
    @ConditionalOnMissingBean(name = "apiFilterChain")
    public SecurityFilterChain apiFilterChain(HttpSecurity http) throws Exception {
        http
                .authenticationProvider(tokenAuthenticationProvider())
                .httpBasic(configurer -> configurer.authenticationEntryPoint(tokenAuthenticationEntryPoint()))
                .sessionManagement(configurer -> configurer.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .requestMatchers()
                .antMatchers("/rest/**")
            .and()
                .authorizeRequests()
                .antMatchers( "/rest/login", "/rest/logout").permitAll()
                .anyRequest().authenticated()
            .and()
                .logout(AbstractHttpConfigurer::disable)
                .formLogin(AbstractHttpConfigurer::disable)
                .csrf(AbstractHttpConfigurer::disable)
                .apply(AuthenticationFilterDsl.custom(tokenAuthenticationEntryPoint()));
        return http.build();
    }


    public static class AuthenticationFilterDsl extends AbstractHttpConfigurer<AuthenticationFilterDsl, HttpSecurity> {

        private final TokenAuthenticationEntryPoint entryPoint;

        public AuthenticationFilterDsl(TokenAuthenticationEntryPoint entryPoint) {
            this.entryPoint = entryPoint;
        }

        @Override
        public void configure(HttpSecurity http) {
            AuthenticationManager authenticationManager = http.getSharedObject(AuthenticationManager.class);
            http.addFilterBefore(new TokenAuthenticationFilter(authenticationManager, entryPoint), UsernamePasswordAuthenticationFilter.class);
        }

        public static AuthenticationFilterDsl custom(TokenAuthenticationEntryPoint entryPoint) {
            return new AuthenticationFilterDsl(entryPoint);
        }
    }
}
